package ua.maxicash.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import ua.maxicash.dto.*;
import ua.maxicash.service.AppService;
import ua.maxicash.service.ConfirmationService;
import ua.maxicash.service.GeoService;
import ua.maxicash.service.WorkService;
import ua.maxicash.util.JqgridQuery;
import ua.maxicash.vo.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Date;
import java.sql.Time;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Controller
public class ApplicationController {

    private static final Logger logger = Logger.getLogger(ApplicationController.class);

    @Autowired
    WorkService workService;

    @Autowired
    AppService appService;

    @Autowired
    ConfirmationService confirmationService;

    @Autowired
    GeoService geoService;

    @PreAuthorize("hasRole('ROLE_EMPLOYER')")
    @RequestMapping("/works/new")
    public String createWork(Model model) {

        model.addAttribute("jobTypes", workService.listJobTypes());
        return "createWork";

    }

    //todo check if this work is owned by right employer
    @PreAuthorize("hasRole('ROLE_EMPLOYER')")
    @RequestMapping("/works/{idWork}/edit")
    public String editWork(@PathVariable Long idWork, Model model) {

        // todo retrieve ONLY Employers
        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserByEmail(user.getUsername());
        Work work = workService.getWorkById(idWork);

        if (su instanceof Employer && work.getEmployer().equals(su)) {

            model.addAttribute("jobTypes", workService.listJobTypes());
            model.addAttribute("work", work);

            logger.info(su.getEmail() + " redirected to the editWork page");
            return "editWork";
        }

        logger.info(su.getEmail() + " is not an instance of Employer");
        return null;
    }

//    @PreAuthorize("hasRole('ROLE_WORKER')")
//    @RequestMapping("/editWorker")
//    public ModelAndView editWorker(
//            @RequestParam("emailWorker") String emailWorker) {
//
//        User user = (User) SecurityContextHolder.getContext()
//                .getAuthentication().getPrincipal();
//        SystemUser su = appService.getSystemUserByEmail(user.getUsername());
//
//        if ((su.getEmail().equals(emailWorker.trim()))
//                && (su instanceof Worker)) {
//
//            Worker worker = (Worker) su;
//            ModelAndView mav = new ModelAndView("editWorker");
//            Set<JobType> jobTypes = new HashSet<JobType>();
//            Set<JobType> workersJobTypes = new HashSet<JobType>();
//            jobTypes.addAll(workService.listJobTypes());
//            workersJobTypes = worker.getJobTypes();
//            jobTypes.removeAll(workersJobTypes);
//
//            mav.addObject("jobTypes", jobTypes);
//            mav.addObject("workersJobTypes", workersJobTypes);
//            mav.addObject("worker", worker);
//
//            for (WorkersFreeTime wft : worker.getScheduleList()) {
//                switch (wft.getDayOfWeek()) {
//                    case 0:
//                        mav.addObject("monday", wft);
//                        break;
//                    case 1:
//                        mav.addObject("tuesday", wft);
//                        break;
//                    case 2:
//                        mav.addObject("wednesday", wft);
//                        break;
//                    case 3:
//                        mav.addObject("thursday", wft);
//                        break;
//                    case 4:
//                        mav.addObject("friday", wft);
//                        break;
//                    case 5:
//                        mav.addObject("saturday", wft);
//                        break;
//                    case 6:
//                        mav.addObject("sunday", wft);
//                        break;
//                    default:
//                        break;
//
//                }
//            }
//
//            logger.info(su.getEmail() + " redirected to the editWorker page");
//            return mav;
//        }
//
//        logger.error(su.getEmail()
//                + " is not an instance of Worker or tried to edit not his page");
//        return null;
//    }

    @PreAuthorize("hasRole('ROLE_EMPLOYER')")
    @RequestMapping("/employers/{idEmployer}/edit")
    public ModelAndView editEmployer(
            @PathVariable Long idEmployer) {

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserById(idEmployer);

        if ((su.getEmail().equals(user.getUsername()))
                && (su instanceof Employer)) {

            Employer employer = (Employer) su;
            ModelAndView mav = new ModelAndView("editEmployer");
            Set<JobType> jobTypes = new HashSet<JobType>();
            Set<JobType> employersJobTypes = new HashSet<JobType>();
            jobTypes.addAll(workService.listJobTypes());
            employersJobTypes = employer.getJobTypes();
            jobTypes.removeAll(employersJobTypes);
            mav.addObject("countries", geoService.getAllCountries());
            mav.addObject("jobTypes", jobTypes);
            mav.addObject("employersJobTypes", employersJobTypes);
            mav.addObject("employer", employer);

            logger.info(su.getEmail() + " redirected to the editEmployer page");
            return mav;
        }

        logger.error(su.getEmail()
                + " is not an instance of Employer or tried to edit not his page");
        return null;
    }

    // TODO EDIT WORK!!!
    // FIXME PLEASE
    @PreAuthorize("hasRole('ROLE_EMPLOYER')")
    @RequestMapping("/saveEditedWork")
    public String saveEditedWork(@RequestParam("idWork") String idWork,
                                 @RequestParam("city") String idCity,
                                 @RequestParam("jobType") String jobType,
                                 @RequestParam("about") String about,
                                 @RequestParam("wage") String wage,
                                 @RequestParam("date") String[] dates,
                                 @RequestParam("startTime") String[] startTimes,
                                 @RequestParam("endTime") String[] endTimes,
                                 HttpServletRequest request) {
        // todo retrieve ONLY Employers

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserByEmail(user.getUsername());

        if (su instanceof Employer) {

            //TODO retrieve work from DB first
            //Work work = new Work();
            //work.setIdWork(Long.parseLong(idWork));
            Work work = workService.getWorkById(Long.parseLong(idWork));

            //work.setEmployer((Employer) su);
            work.setCity(geoService.getCityById(Long.parseLong(idCity)));
            work.setAbout(about);
            work.setJobType(workService.selectJobTypeById(Long
                    .parseLong(jobType)));
            work.setWage(Double.parseDouble(wage));
            Set<WorkingDay> workingDays = new HashSet<WorkingDay>();

            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
            for (int i = 0; i < dates.length; i++) {
                WorkingDay wd = new WorkingDay();

                try {
                    wd.setWorkDate(new Date((dateFormat.parse(dates[i])
                            .getTime())));
                } catch (ParseException e) {
                    logger.error(e);
                }
                try {
                    wd.setStartTime(new Time(timeFormat.parse(startTimes[i])
                            .getTime()));
                } catch (ParseException e) {
                    logger.error(e);
                }
                try {
                    wd.setEndTime(new Time(timeFormat.parse(endTimes[i])
                            .getTime()));
                } catch (ParseException e) {
                    logger.error(e);
                }
                workingDays.add(wd);

            }

            work.setSchedule(workingDays);
            workService.updateWork(work);

            logger.info(su.getEmail() + " has updated the work : "
                    + work.getIdWork()
                    + " and is redirecting to the showWorks page");
            return "redirect:/works/" + work.getIdWork();
        }
        return null;
    }

    @PreAuthorize("hasRole('ROLE_EMPLOYER')")
    @RequestMapping("/works/create")
    public String createWork(@RequestParam("city") String idCity,
                             @RequestParam("jobType") String jobType,
                             @RequestParam("about") String about,
                             @RequestParam("wage") String wage,
                             @RequestParam("date") String[] dates,
                             @RequestParam("startTime") String[] startTimes,
                             @RequestParam("endTime") String[] endTimes,
                             HttpServletRequest request) {
        // todo retrieve ONLY Employers
        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserByEmail(user.getUsername());

        if (su instanceof Employer) {

            Work work = new Work();

            work.setEmployer((Employer) su);
            work.setCity(geoService.getCityById(Long.parseLong(idCity)));
            work.setAbout(about);
            work.setJobType(workService.selectJobTypeById(Long
                    .parseLong(jobType)));
            work.setWage(Double.parseDouble(wage));
            Set<WorkingDay> workingDays = new HashSet<WorkingDay>();

            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
            for (int i = 0; i < dates.length; i++) {
                WorkingDay wd = new WorkingDay();

                try {
                    wd.setWorkDate(new Date((dateFormat.parse(dates[i])
                            .getTime())));
                } catch (ParseException e) {
                    logger.error(e);
                }
                try {
                    wd.setStartTime(new Time(timeFormat.parse(startTimes[i])
                            .getTime()));
                } catch (ParseException e) {
                    logger.error(e);
                }
                try {
                    wd.setEndTime(new Time(timeFormat.parse(endTimes[i])
                            .getTime()));
                } catch (ParseException e) {
                    logger.error(e);
                }
                workingDays.add(wd);

            }

            work.setSchedule(workingDays);
            workService.addWork(work);

            logger.info(su.getEmail()
                    + " has created the work and is redirecting to the showWorks page");
            return "redirect:/works/" + work.getIdWork();
        }

        logger.error(su.getEmail()
                + " is not an instance of Employer tried to create new work");
        return null;
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/works")
    public String showWorks(Model model) {

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserByEmail(user.getUsername());

        Random rand = new Random(System.currentTimeMillis());
        model.addAttribute("city", su.getCity().getRuName());
        //retrieve random preferred job type
        model.addAttribute("preferredJobType", su.getJobTypes().toArray()[rand.nextInt(su.getJobTypes().size())]);

        logger.info(su.getEmail() + " is redirecting to the works page");
        return "worksPage";
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/works/{idWork}")
    public String showWork(@PathVariable Long idWork, Model model) {

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        SystemUser su = appService.getSystemUserByEmail(user.getUsername());

        Work work = workService.getWorkById(idWork);
        model.addAttribute("work", work);

        if (su instanceof Worker) {
            model.addAttribute("isSubscribed", appService.isSubscribed((Worker) su, work));
        }

        logger.info(su.getEmail() + " is redirecting to the worksPage page");
        return "workPage";
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/data/worksData")
    @ResponseBody
    public WorksJsonDto getWorksInJson(
            @RequestParam(value = "filters", required = false) String filters) {

        WorksJsonDto wjd = new WorksJsonDto();
        wjd.setPage("1");
        wjd.setRecords("5");
        wjd.setTotal("20");

        Set<Work> works = null;

        if (filters != null && !filters.isEmpty()) {
            JqgridQuery jq = appService.parseFilters(filters);
            works = appService.getWorksByJqgridQuery(jq);
        } else {
            works = appService.getAllWorks();
        }

        List<WorkRowJson> rowJsonList = new ArrayList<WorkRowJson>();

        for (Work work : works) {
            for (WorkingDay wd : work.getSchedule()) {
                rowJsonList.add(appService.convertWorkingDayToJsonDto(wd));
            }
        }
        wjd.setRows(rowJsonList);

        return wjd;
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/data/workersData")
    @ResponseBody
    public WorkersJsonDto getWorkersInJson(
            @RequestParam(value = "filters", required = false) String filters) {

        WorkersJsonDto wjd = new WorkersJsonDto();
        wjd.setPage("1");
        wjd.setRecords("5");
        wjd.setTotal("20");

        Set<Worker> workers = null;

        if (filters != null && !filters.isEmpty()) {
            JqgridQuery jq = appService.parseFilters(filters);
            workers = appService.getWorkersByJqgridQuery(jq);
        } else {
            workers = appService.getAllWorkers();
        }

        List<WorkerRowJson> rowJsonList = new ArrayList<WorkerRowJson>();

        for (Worker worker : workers) {
            rowJsonList.add(appService.convertWorkerToRowJson(worker));
        }

        wjd.setRows(rowJsonList);

        return wjd;

    }

    @PreAuthorize("hasRole('ROLE_WORKER')")
    @RequestMapping(value = "/works/{idWork}/subscribe", method = RequestMethod.GET)
    @ResponseBody
    public String subscribeOnWork(@PathVariable Long idWork) {

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        try {
            appService.subscribeWorker(user.getUsername(), idWork);
            logger.info(user.getUsername() + " has successfully subscribed for work " + idWork);
            return "{\"isOk\" : true }";
        } catch (Throwable t) {
            logger.error(t);
            return "{\"isOk\" : false }";
        }

    }

    @PreAuthorize("hasRole('ROLE_WORKER')")
    @RequestMapping(value = "/works/{idWork}/unsubscribe", method = RequestMethod.GET)
    @ResponseBody
    public String unsubscribeOnWork(@PathVariable Long idWork) {

        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        try {
            appService.unsubscribeWorker(user.getUsername(), idWork);
            logger.info(user.getUsername() + " has successfully unsubscribed from work " + idWork);
            return "{\"isOk\" : true }";
        } catch (Throwable t) {
            logger.error(t);
            return "{\"isOk\" : false }";
        }

    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/data/subscribedWorkersData")
    @ResponseBody
    public WorkersOnWorkJsonDto getSubscribedWorkers(@RequestParam("idWork") Long idWork) {

        // TODO implement
        Work work = workService.getWorkById(idWork);
        WorkersOnWorkJsonDto wjd = new WorkersOnWorkJsonDto();
        wjd.setPage("1");
        wjd.setRecords("5");
        wjd.setTotal("20");

        List<WorkerOnWorkRowJson> rowJsonList = new ArrayList<WorkerOnWorkRowJson>();

        for (Worker worker : work.getApplicants()) {
            rowJsonList.add(appService.convertWorkerOnWorkToRowJson(worker));
        }

        wjd.setRows(rowJsonList);

        return wjd;
    }

    @RequestMapping(value = "/selectgeo/get/regions/{idCountry}")
    @ResponseBody
    public Set<RegionJsonDto> getRegions(@PathVariable Long idCountry) {

        Set<RegionJsonDto> regions = new HashSet<RegionJsonDto>();
        for (Region reg : geoService.getAllRegionsOfCountry(idCountry)) {
            regions.add(RegionJsonDto.createFromRegion(reg));
        }

        return regions;

    }

    @RequestMapping(value = "/selectgeo/get/cities/{idRegion}")
    @ResponseBody
    public Set<CityJsonDto> getCities(@PathVariable Long idRegion) {

        Set<CityJsonDto> cities = new HashSet<CityJsonDto>();
        for (City city : geoService.getAllCitiesOfRegion(idRegion)) {
            cities.add(CityJsonDto.createFromCity(city));
        }

        return cities;

    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping(value = "/get/likes")
    @ResponseBody
    public Set<LightSystemUser> getLikes(@RequestParam("amount") int amount,
                                         @RequestParam(value = "idUser") Long idUser) {

        Set<LightSystemUser> users = new HashSet<LightSystemUser>();
        for (SystemUser su : appService.getLikes(idUser, amount)) {
            users.add(LightSystemUser.convert(su));
        }
        return users;
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping(value = "/like")
    @ResponseBody
    public String like(@RequestParam("email") String hisEmail) {
        User user = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();

        appService.like(user.getUsername(), hisEmail);

        return "{\"text\" : \"You've added " + hisEmail + " to your likelist\"} ";
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("users/{idUser}/likes")
    public String getLikeList(@PathVariable Long idUser, Model model) {

        model.addAttribute("systemUser", appService.getSystemUserById(idUser));
        model.addAttribute("likes", appService.getAllLikes(idUser));

        return "likelist";
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/likes/delete")
    @ResponseBody
    public String deleteLike(@RequestParam("user") Long idUser, @RequestParam("likeToDelete") Long idLike, Model model) {

        appService.removeFromLikes(idUser,idLike);

        return "{\"isOk\":true}";
    }

    @PreAuthorize("isAuthenticated()")
    @RequestMapping("/getJobTypes")
    @ResponseBody
    public String getJobTypes() {

        String result = " {\"jobTypes\" : [ ";
        for (JobType jt : workService.listJobTypes()) {
            result = result + "\"" + jt.getTitle() + "\",";
        }
        result = result.substring(0, result.lastIndexOf(","));
        return result + "] }";
    }

    @RequestMapping(value = "users/{idUser}/getImage", method = RequestMethod.GET)
    public void showWorkersImage(@PathVariable Long idUser,
                                 HttpServletRequest req, HttpServletResponse resp) {

        SystemUser su = appService.getSystemUserById(idUser);

        resp.setContentType("image/jpg");
        try {
            if (su.getAvatar() != null) {
                resp.getOutputStream().write(su.getAvatar());
            } else {
                InputStream in = this.getClass().getClassLoader().getResourceAsStream("noAvatar.jpg");
                OutputStream out = resp.getOutputStream();
                int next;
                while ((next = in.read()) != -1)
                    out.write(next);
            }
            resp.getOutputStream().flush();
            resp.getOutputStream().close();
        } catch (IOException e) {
            logger.error(e);
        }

    }


}
